package main

//开发日志:
//02/07/2020：已完成 前端页面：接受并显示文字消息，图片消息，文件消息，
//                         好友列表（登录时查询是否在线），登录账号验证密码
//当前需要完成功能:缓存聊天记录（考虑是在浏览器缓存，还是服务器端数据库）[已完成保存记录，还需要添加时间戳并显示时间],点击好友列表切换当前聊天对象([]用户ID),
//              添加好友功能，语音通话功能，语音消息功能
//02/08/2020：已完成 前端页面：接受并显示文字消息，图片消息，文件消息，
//                         好友列表（登录时查询是否在线），登录账号验证密码
//                         缓存与查询聊天记录，点击好友标签修改当前焦点
//当前需要完成功能:添加好友功能，语音通话功能，语音消息功能，自身在线状态变更广播（上线下线）
//02/09/2020: 已完成 添加好友功能，注册功能（添加好友需先输入添加对象用户名，点击确认，然后点击添加）

import (

	//"crypto/rand"
	"bytes"
	"database/sql"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	rand "math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	_ "github.com/Go-SQL-Driver/MySQL"
	"github.com/gorilla/websocket"
)

//"path/filepath"  "mime"
var clients = make(map[string]*websocket.Conn) // connected clients
var usermap = make(map[string]int)             // connected clients
var UserMessagePool = make(chan Message)       // UserMessagePool channel
const maxUploadSize = 2 * 1024 * 1024          // 2 mb
const uploadPath = "./tmp"

var userSignature int = 0
var fileTypeMap sync.Map

// Configure the upgrader
var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}
var (
	dbhostsip  = "127.0.0.1"
	dbusername = "root"
	//dbpassowrd = "123456"
	dbpassowrd = "123456"
	dbname     = "fastchat"
)

type mysql_db struct {
	Db *sql.DB //定义结构体
}

// Define our message object
type Message struct {
	Password string `json:"password"` //自己的邮箱和用户,现在把邮箱当做密码，以后修改
	Username string `json:"username"`
	Message  string `json:"message"`
	ConType  string `json:"type"`
	SendTime string `json:"sendtime"`
	TimeTick string `json:"timetick"`
}
type Sql_Message struct {
	message_id   string //自己的邮箱和用户,现在把邮箱当做密码，以后修改
	maker_uid    string
	maker_uname  string
	accept_uid   string
	accept_uname string
	msgtype      string
	content      string
	msgtime      string
	timetick     string
}

var global_sql *mysql_db

func (f *mysql_db) mysql_open() { //打开
	Odb, err := sql.Open("mysql", dbusername+":"+dbpassowrd+"@tcp("+dbhostsip+")/"+dbname)
	if err != nil {
		fmt.Println("链接失败")
	}
	fmt.Println("链接数据库成功...........已经打开")
	f.Db = Odb
}

func (f *mysql_db) mysql_close() { //关闭
	defer f.Db.Close()
	fmt.Println("链接数据库成功...........已经关闭")
}

func (f *mysql_db) mysql_select_friend(self_uid string) map[string]string {
	rows, err := f.Db.Query(
		"SELECT accept_uid,accept_uname from user_relationship WHERE maker_uid =?",
		self_uid,
	)
	if err != nil {
		println("mysql_select_friend错误:" + err.Error())
	}
	friend_list := make(map[string]string)
	var i string
	var friendname string
	for rows.Next() {
		rows.Scan(&i, &friendname)
		friend_list[i] = friendname
		fmt.Println("mysql_select_friend函数结果：" + i + ":" + friendname)
	}
	fmt.Println(rows)
	return friend_list
}
func (f *mysql_db) mysql_password(username string) string {
	rows, err := f.Db.Query(
		"SELECT password from user WHERE username =? limit 1",
		username,
	)
	if err != nil {
		println(err)
	}

	fmt.Println(rows)
	true_password := ""
	for rows.Next() {
		rows.Scan(&true_password)
		fmt.Println(true_password)
	}
	return true_password
}
func (f *mysql_db) mysql_uid(username string) string {
	rows, err := f.Db.Query(
		"SELECT uid from user WHERE username =? limit 1",
		username,
	)
	if err != nil {
		println(err)
	}

	fmt.Println(rows)
	uid := ""
	for rows.Next() {
		rows.Scan(&uid)
		fmt.Println("查询得出用户ID" + uid)
	}
	return uid
}
func (f *mysql_db) mysql_register(username string, password string) string {

	result, err := f.Db.Exec(
		"INSERT INTO user (username,password) VALUES (?, ?)",
		username,
		password, //fastchat用户注册
	)
	if err != nil {

		return "用户名已占用"
	}
	fmt.Println("注册用户昵称" + username + "密码" + password)
	fmt.Println(result)
	fmt.Println(err)
	return "注册成功"
}
func (f *mysql_db) mysql_addfriend(maker_uid string, accept_uid string, accept_uname string) string {

	result, err := f.Db.Exec(
		"INSERT INTO user_relationship (maker_uid,accept_uid,accept_uname) VALUES (?, ?, ?)",
		maker_uid,
		accept_uid, //fastchat添加好友
		accept_uname,
	)
	if err != nil {

		return "用户名不存在或对方拒绝了你"
	}
	fmt.Println("用户ID" + maker_uid + "对方ID" + accept_uid)
	fmt.Println(result)
	fmt.Println(err)
	return "添加成功"
}
func (f *mysql_db) mysql_save_message(msg Message, maker_uid string, accept_uid string) bool {
	var UserTarget string = ""
	if strings.Contains(msg.Message, "[-]") {
		AllMsg := strings.Split(msg.Message, "[-]")
		//UserMsg := AllMsg[0]
		UserTarget = AllMsg[1]
		fmt.Println("------保存目标用户的名字：" + UserTarget)
	}
	result, err := f.Db.Exec(
		"INSERT INTO all_message (maker_uid,maker_uname,accept_uid,accept_uname,type,content,time,timetick) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
		maker_uid,
		msg.Username, //fastchat消息保存
		accept_uid,
		UserTarget,
		msg.ConType,
		msg.Message,
		msg.SendTime,
		msg.TimeTick,
	)
	if err != nil {
		fmt.Println(err.Error())
		return false
	}
	fmt.Println(result)
	return true
}
func (f *mysql_db) mysql_select_message(maker_uname string, accept_uname string) map[int]Sql_Message {
	message_list := make(map[int]Sql_Message)
	rows, err := f.Db.Query(
		"SELECT message_id,maker_uid,accept_uid,type,content,time,timetick from all_message where maker_uname=? and accept_uname=?",
		maker_uname,
		accept_uname, //fastchat历史消息查询
	)
	if err != nil {
		fmt.Println(err.Error())
		var false_select_msg Sql_Message
		message_list[-1] = false_select_msg
		return message_list
	}

	var one_message Sql_Message
	msg_count := 0
	for rows.Next() {
		rows.Scan(&one_message.message_id, &one_message.maker_uid, &one_message.accept_uid, &one_message.msgtype, &one_message.content, &one_message.msgtime, &one_message.timetick)
		one_message.maker_uname = maker_uname
		one_message.accept_uname = accept_uname
		msg_id, err := strconv.Atoi(one_message.message_id)
		if nil != err {

		}
		msg_count++
		message_list[msg_id] = one_message
		fmt.Println("mysql_select_message函数结果：" + one_message.message_id + ":" + one_message.content)
	}

	return message_list

}

func main() {
	// Create a simple file server
	fs := http.FileServer(http.Dir("../public"))
	http.Handle("/", fs)

	// Configure websocket route
	http.HandleFunc("/ws", handleConnections)

	// Start listening for incoming chat messages
	f := &mysql_db{} //声明mysql结构体
	f.mysql_open()   //连接池对象打开
	global_sql = f
	go handleMessages(f)

	http.HandleFunc("/addfriend", handleAddfriend)
	http.HandleFunc("/upload", uploadFileHandler())
	http.HandleFunc("/oldmsg", handleOldMsg)
	http.HandleFunc("/download", DownloadFile)
	http.HandleFunc("/img", DisplayImg)
	log.Print("Server started on localhost:8080, use /upload for uploading files and /files/{fileName} for downloading")
	// Start the server on localhost port 8000 and log any errors
	log.Println("http server started on :8000")
	//test()
	http.ListenAndServe(":8000", nil)
}
func test() {
	var msga Message
	msga.Username = "123"
	msga.Password = "123"
	msga.Message = "rty[-]123"
	msga.ConType = "message"
	//Return_Friendlist(msga, global_sql)
	//global_sql.mysql_register("rty", "dfg")
	//global_sql.mysql_save_message(msga, "1", "1")
	global_sql.mysql_select_message("123", "123")
}
func handleConnections(w http.ResponseWriter, r *http.Request) {
	// Upgrade initial GET request to a websocket
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Fatal(err)
	}
	// Make sure we close the connection when the function returns
	defer ws.Close()
	// Register our new client
	var loginmsg Message
	err = ws.ReadJSON(&loginmsg)
	if err != nil {
		log.Printf("error: %v", err)
	}
	GetPassword := loginmsg.Password //后期改用md5存储
	if "register" == loginmsg.ConType {
		register_res := global_sql.mysql_register(loginmsg.Username, loginmsg.Password)
		loginmsg.Message = register_res
		err := ws.WriteJSON(loginmsg)
		if err != nil {
			log.Printf("error: %v", err)
			ws.Close()
			//delete(clients, UserTarget)
		}

	} else {
		TruePassword := global_sql.mysql_password(loginmsg.Username)
		fmt.Println("用户名：" + loginmsg.Username + "用户发送的密码：" + GetPassword + "真实密码:" + TruePassword)
		if GetPassword != TruePassword {
			//登录失败，返回失败提示（用msg封装）
			err := ws.WriteJSON(Return_LoginReject(loginmsg))
			if err != nil {
				log.Printf("error: %v", err)
				ws.Close()
				//delete(clients, UserTarget)
			}
		} else {
			//登录成功，返回好友列表（用msg封装）
			clients[loginmsg.Username] = ws
			err := ws.WriteJSON(Return_Friendlist(loginmsg, global_sql))
			if err != nil {
				log.Printf("error: %v", err)
				ws.Close()
				//delete(clients, UserTarget)
			}
		}
	}

	for {
		var msg Message
		// Read in a new message as JSON and map it to a Message object
		err := ws.ReadJSON(&msg)
		if err != nil {
			log.Printf("error: %v", err)
			delete(clients, msg.Username)
			break
		}
		// Send the newly received message to the UserMessagePool channel
		UserMessagePool <- msg
	}
}

func handleMessages(f *mysql_db) {
	for {
		// Grab the next message from the UserMessagePool channel
		msg := <-UserMessagePool
		fmt.Println("请求类型：" + msg.ConType)

		if strings.Contains(msg.Message, "[-]") {
			AllMsg := strings.Split(msg.Message, "[-]")
			UserMsg := AllMsg[0]
			UserTarget := AllMsg[1]
			fmt.Println(UserMsg + "------目标用户的名字：" + UserTarget)

			var maker_uid string = f.mysql_uid(msg.Username)
			var accept_uid string = f.mysql_uid(UserTarget)
			time_tick := time.Now().Unix()
			formatTimeStr := time.Unix(time_tick, 0).Format("2006-01-02 15:04:05")
			msg.SendTime = formatTimeStr
			msg.TimeTick = strconv.FormatInt(time_tick, 10)
			f.mysql_save_message(msg, maker_uid, accept_uid)

			msg.Message = UserMsg
			if nil != clients[UserTarget] {
				err := clients[UserTarget].WriteJSON(msg)
				if err != nil {
					log.Printf("error: %v", err)
					clients[UserTarget].Close()
					delete(clients, UserTarget)
				}
			}
		}

	}
}
func handleOldMsg(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var selfname string = r.Form["selfname"][0]
	var targetname string = r.Form["targetname"][0]
	Return_history_message(selfname, targetname)

}
func handleAddfriend(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	var selfname string = r.Form["selfname"][0]
	var targetname string = r.Form["targetname"][0]
	self_uid := global_sql.mysql_uid(selfname)
	target_uid := global_sql.mysql_uid(targetname)
	add_res := global_sql.mysql_addfriend(self_uid, target_uid, targetname)
	var tmp_msg Message
	tmp_msg.ConType = "Addfriend_res"
	tmp_msg.Message = add_res
	err := clients[selfname].WriteJSON(&tmp_msg)
	if err != nil {
		log.Printf("error: %v", err.Error())
		clients[selfname].Close()
		//delete(clients, UserTarget)
	}
	var loginmsg Message
	loginmsg.ConType = "login_success"
	loginmsg.Username = selfname
	err = clients[selfname].WriteJSON(Return_Friendlist(loginmsg, global_sql))
	if err != nil {
		log.Printf("error: %v", err)
		clients[selfname].Close()
		//delete(clients, UserTarget)
	}

}
func uploadFileHandler() http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// validate file size

		reader, err := r.MultipartReader()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		UnixTime := time.Now().Unix()
		RndomSource := rand.NewSource(UnixTime)
		randomPort := rand.New(RndomSource)
		filepre := 10000 + randomPort.Intn(1000)
		var filename string = "0"
		var isFileCreated bool = false
		//传递消息给用户456
		for {
			part, err := reader.NextPart()
			if err == io.EOF {
				break
			}

			fmt.Printf("FileName=[%s], FormName=[%s]\n", part.FileName(), part.FormName())

			if part.FileName() == "" { // 无文件名多次传输二进制数据
				data, _ := ioutil.ReadAll(part)
				fmt.Printf("FormData=[%s]\n", string(data))
			} else { // 这里创建文件，只执行一次的
				var dst *os.File
				if true != isFileCreated {
					filename = strconv.Itoa(filepre) + part.FileName()
					dst, _ = os.Create("../public/" + filename)
					isFileCreated = true
					defer dst.Close()
				}
				io.Copy(dst, part)
			}
		}

		r.ParseForm()
		var filetype string = r.Form["type"][0]
		//var selfname string = r.Form["selfname"][0]
		var targetname string = r.Form["targetname"][0]
		fmt.Println("requesttype=[%s]]\n", filetype)
		sendFileurl(filetype, filename, targetname)
	})
}
func DownloadFile(rw http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	var filepath string = "../public/" + r.Form["filename"][0]
	file, err := os.Open(filepath)
	if err != nil {
		fmt.Println(err.Error())
	}
	defer file.Close()
	//fileName := path.Base(filepath)
	//fileName = url.QueryEscape(fileName) // 防止中文乱码
	//fileName = fileName[strings.LastIndex(fileName, string(fileFullPath.Separator))+1:]
	rw.Header().Add("Content-Type", "multipart/form-data")
	rw.Header().Add("Content-Disposition:", "attachment; filename=\""+r.Form["filename"][0]+"\"")
	rw.Header().Set("Content-Type", "application/octet-stream")
	//rw.Header().Set("content-Disposition", fmt.Sprintf("attachment;filename=%s", r.Form["filename"][0]))
	rw.Header().Add("Content-Transfer-Encoding", "binary")
	rw.Header().Add("Expires", "0")
	rw.Header().Add("Cache-Control", "must-revalidate")
	rw.Header().Add("Pragma", "public")
	_, error := io.Copy(rw, file)
	if error != nil {
		fmt.Println(err.Error())
	}
}
func DisplayImg(rw http.ResponseWriter, r *http.Request) {
	r.ParseForm()

	var filepath string = "../public/" + r.Form["filename"][0]
	fmt.Println("display imgs-line 192")
	file, err := os.Open(filepath)
	if err != nil {
		fmt.Println(err.Error())
	}
	defer file.Close()
	//fileName := path.Base(filepath)
	//fileName = url.QueryEscape(fileName) // 防止中文乱码
	//fileName = fileName[strings.LastIndex(fileName, string(fileFullPath.Separator))+1:]
	//rw.Header().Add("Content-Type", "multipart/form-data")
	//rw.Header().Add("Content-Disposition:", "attachment; filename=\""+r.Form["filename"][0]+"\"")
	//rw.Header().Set("Content-Type", "application/octet-stream")
	//rw.Header().Set("content-Disposition", fmt.Sprintf("attachment;filename=%s", r.Form["filename"][0]))
	//rw.Header().Add("Content-Transfer-Encoding", "binary")
	//rw.Header().Add("Expires", "0")
	//rw.Header().Add("Cache-Control", "must-revalidate")
	//rw.Header().Add("Pragma", "public")
	_, error := io.Copy(rw, file)
	if error != nil {
		fmt.Println(err.Error())
	}
}
func sendFileurl(requestType string, filename string, username string) {
	filetype := FastGetFileType("../public/" + filename)
	var msg Message
	msg.Username = "123" //固定发起用户123
	msg.Password = "123"
	if "" != filetype {
		if requestType == "img" {
			if filetype == "jpg" || filetype == "png" || filetype == "gif" || filetype == "bmp" {
				msg.ConType = "img"
				fmt.Println("display imgs-line 223")
			} else {
				msg.ConType = "file"
			}
		} else {
			msg.ConType = "file"
		}

	} else {
		msg.ConType = "file"
	}

	fmt.Printf("Filetype=[%s]", filetype)
	msg.Message = filename + "[-]" + username //固定目标用户456，后期改为参数传入
	UserMessagePool <- msg
	fmt.Printf("send end")
	return
}
func renderError(w http.ResponseWriter, message string, statusCode int) {
	w.WriteHeader(http.StatusBadRequest)
	w.Write([]byte(message))
}

func randToken(len int) string {
	b := make([]byte, len)
	rand.Read(b)
	return fmt.Sprintf("%x", b)
}

func init() {
	fileTypeMap.Store("ffd8ffe000104a464946", "jpg")  //JPEG (jpg)
	fileTypeMap.Store("89504e470d0a1a0a0000", "png")  //PNG (png)
	fileTypeMap.Store("47494638396126026f01", "gif")  //GIF (gif)
	fileTypeMap.Store("49492a00227105008037", "tif")  //TIFF (tif)
	fileTypeMap.Store("424d228c010000000000", "bmp")  //16色位图(bmp)
	fileTypeMap.Store("424d8240090000000000", "bmp")  //24位位图(bmp)
	fileTypeMap.Store("424d8e1b030000000000", "bmp")  //256色位图(bmp)
	fileTypeMap.Store("41433130313500000000", "dwg")  //CAD (dwg)
	fileTypeMap.Store("3c21444f435459504520", "html") //HTML (html)   3c68746d6c3e0  3c68746d6c3e0
	fileTypeMap.Store("3c68746d6c3e0", "html")        //HTML (html)   3c68746d6c3e0  3c68746d6c3e0
	fileTypeMap.Store("3c21646f637479706520", "htm")  //HTM (htm)
	fileTypeMap.Store("48544d4c207b0d0a0942", "css")  //css
	fileTypeMap.Store("696b2e71623d696b2e71", "js")   //js
	fileTypeMap.Store("7b5c727466315c616e73", "rtf")  //Rich Text Format (rtf)
	fileTypeMap.Store("38425053000100000000", "psd")  //Photoshop (psd)
	fileTypeMap.Store("46726f6d3a203d3f6762", "eml")  //Password [Outlook Express 6] (eml)
	fileTypeMap.Store("d0cf11e0a1b11ae10000", "doc")  //MS Excel 注意：word、msi 和 excel的文件头一样
	fileTypeMap.Store("d0cf11e0a1b11ae10000", "vsd")  //Visio 绘图
	fileTypeMap.Store("5374616E64617264204A", "mdb")  //MS Access (mdb)
	fileTypeMap.Store("252150532D41646F6265", "ps")
	fileTypeMap.Store("255044462d312e350d0a", "pdf")  //Adobe Acrobat (pdf)
	fileTypeMap.Store("2e524d46000000120001", "rmvb") //rmvb/rm相同
	fileTypeMap.Store("464c5601050000000900", "flv")  //flv与f4v相同
	fileTypeMap.Store("00000020667479706d70", "mp4")
	fileTypeMap.Store("49443303000000002176", "mp3")
	fileTypeMap.Store("000001ba210001000180", "mpg") //
	fileTypeMap.Store("3026b2758e66cf11a6d9", "wmv") //wmv与asf相同
	fileTypeMap.Store("52494646e27807005741", "wav") //Wave (wav)
	fileTypeMap.Store("52494646d07d60074156", "avi")
	fileTypeMap.Store("4d546864000000060001", "mid") //MIDI (mid)
	fileTypeMap.Store("504b0304140000000800", "zip")
	fileTypeMap.Store("526172211a0700cf9073", "rar")
	fileTypeMap.Store("235468697320636f6e66", "ini")
	fileTypeMap.Store("504b03040a0000000000", "jar")
	fileTypeMap.Store("4d5a9000030000000400", "exe")        //可执行文件
	fileTypeMap.Store("3c25402070616765206c", "jsp")        //jsp文件
	fileTypeMap.Store("4d616e69666573742d56", "mf")         //MF文件
	fileTypeMap.Store("3c3f786d6c2076657273", "xml")        //xml文件
	fileTypeMap.Store("494e5345525420494e54", "sql")        //xml文件
	fileTypeMap.Store("7061636b616765207765", "java")       //java文件
	fileTypeMap.Store("406563686f206f66660d", "bat")        //bat文件
	fileTypeMap.Store("1f8b0800000000000000", "gz")         //gz文件
	fileTypeMap.Store("6c6f67346a2e726f6f74", "properties") //bat文件
	fileTypeMap.Store("cafebabe0000002e0041", "class")      //bat文件
	fileTypeMap.Store("49545346030000006000", "chm")        //bat文件
	fileTypeMap.Store("04000000010000001300", "mxp")        //bat文件
	fileTypeMap.Store("504b0304140006000800", "docx")       //docx文件
	fileTypeMap.Store("d0cf11e0a1b11ae10000", "wps")        //WPS文字wps、表格et、演示dps都是一样的
	fileTypeMap.Store("6431303a637265617465", "torrent")
	fileTypeMap.Store("6D6F6F76", "mov")         //Quicktime (mov)
	fileTypeMap.Store("FF575043", "wpd")         //WordPerfect (wpd)
	fileTypeMap.Store("CFAD12FEC5FD746F", "dbx") //Outlook Express (dbx)
	fileTypeMap.Store("2142444E", "pst")         //Outlook (pst)
	fileTypeMap.Store("AC9EBD8F", "qdf")         //Quicken (qdf)
	fileTypeMap.Store("E3828596", "pwl")         //Windows Password (pwl)
	fileTypeMap.Store("2E7261FD", "ram")         //Real Audio (ram)
}

// 获取前面结果字节的二进制
func bytesToHexString(src []byte) string {
	res := bytes.Buffer{}
	if src == nil || len(src) <= 0 {
		return ""
	}
	temp := make([]byte, 0)
	for _, v := range src {
		sub := v & 0xFF
		hv := hex.EncodeToString(append(temp, sub))
		if len(hv) < 2 {
			res.WriteString(strconv.FormatInt(int64(0), 10))
		}
		res.WriteString(hv)
	}
	return res.String()
}

// 用文件前面几个字节来判断
// fSrc: 文件字节流（就用前面几个字节）
func GetFileType(fSrc []byte) string {
	var fileType string
	fileCode := bytesToHexString(fSrc)

	fileTypeMap.Range(func(key, value interface{}) bool {
		k := key.(string)
		v := value.(string)
		if strings.HasPrefix(fileCode, strings.ToLower(k)) ||
			strings.HasPrefix(k, strings.ToLower(fileCode)) {
			fileType = v
			return false
		}
		return true
	})
	return fileType
}

func FastGetFileType(filepath string) string {

	f, err := os.Open(filepath)
	if err != nil {
		fmt.Println("open error: %v", err)
	}

	fSrc, err := ioutil.ReadAll(f)
	filetype := GetFileType(fSrc[:10])
	return filetype
}
func Return_LoginReject(msg Message) Message {
	msg.Message = "登录失败"
	msg.ConType = "login_false"
	msg.Password = "login"
	msg.Username = "login"
	return msg
}
func Return_Friendlist(msg Message, f *mysql_db) Message {
	var uid string = f.mysql_uid(msg.Username)
	var local_friend_list map[string]string = f.mysql_select_friend(uid)
	//msg.Message = "" //直接在服务器端把页面写好
	msg.ConType = "login_success"
	var Message_head string = "<ul class=\"box\"><li><span>我的好友</span> <div name=\"list\" class=\"show\">"
	var Message_tail string = " </div></li>"
	var Message_body string = ""
	for uid := range local_friend_list {
		fmt.Println(uid)
		var online_ornot string = "（不在线）"
		_, res := clients[local_friend_list[uid]]
		if true == res {
			online_ornot = "（在线）"
		}
		Message_body += "<button class=\"waves-effect\" onclick=\"friend(" + local_friend_list[uid] + ")\">" + local_friend_list[uid] + online_ornot + "</button>"

	}
	msg.Message = Message_head + Message_body + Message_tail
	fmt.Println("返回的好友列表: %v", msg.Message)
	return msg
}

//对某个用户名查询是否在线
func Return_username_online_status(username string) bool {
	var online_res bool = false
	if nil != clients[username].LocalAddr() {
		online_res = true
	}
	return online_res
}

//返回所有列表好友的在线情况
func Return_allfriend_online_status(selfname string) map[string]string {
	var uid string = global_sql.mysql_uid(selfname)
	var local_friend_list map[string]string = global_sql.mysql_select_friend(uid)
	local_friend_online := make(map[string]string)
	for uid := range local_friend_list {
		fmt.Println(uid)
		local_username := local_friend_list[uid]
		local_friend_online[local_username] = "不在线" //先固定写成不在线，后传入探测结果
	}
	return local_friend_online //返回在线检测结果
}
func Return_history_message(selfname string, targetname string) {
	self_send_message := global_sql.mysql_select_message(selfname, targetname)
	all_history_message := global_sql.mysql_select_message(targetname, selfname)
	var msg_count int = 0
	var tmp_msg Message
	var tmp_sqlmsg Sql_Message
	//合并自己发出的消息和接受的消息
	for k, _ := range all_history_message {
		k = k
		msg_count++
	} //合并自己发出的消息和接受的消息
	for k, v := range self_send_message {
		all_history_message[k] = v
		msg_count++
	}
	_, res := clients[selfname]
	if true == res {
		sort_res := make([]int, msg_count)
		i := 0
		for k, _ := range all_history_message {
			sort_res[i] = k
			i++
		} //合并自己发出的消息和接受的消息
		sort_length := len(sort_res) - 1
		quickSort(sort_res, 0, sort_length)
		msg_id := 0
		for sort_id := 0; sort_id <= sort_length; sort_id++ {
			msg_id = sort_res[sort_id]
			tmp_sqlmsg = all_history_message[msg_id]
			tmp_msg.ConType = tmp_sqlmsg.msgtype
			tmp_msg.SendTime = tmp_sqlmsg.msgtime
			tmp_msg.Username = tmp_sqlmsg.maker_uname
			tmp_msg.Message = tmp_sqlmsg.content
			tmp_msg.TimeTick = tmp_sqlmsg.timetick
			err := clients[selfname].WriteJSON(&tmp_msg)
			if err != nil {
				log.Printf("error: %v", err.Error())
				clients[selfname].Close()
				//delete(clients, UserTarget)
			}
		}
	}

}
func quickSort(arr []int, start int, end int) {
	if start < end {
		i, j := start, end
		key := arr[(start+end)/2]
		for i <= j {
			for arr[i] < key {
				i++
			}
			for arr[j] > key {
				j--
			}
			if i <= j {
				arr[i], arr[j] = arr[j], arr[i]
				i++
				j--
			}
		}

		if start < j {
			quickSort(arr, start, j)
		}
		if end > i {
			quickSort(arr, i, end)
		}
	}
}
